import Fraction from "./fraction"
import Int from "./int"
import Rational from "./rational"

class Decimal extends Rational {
    /**
     * @type {{integer: BigInt, decimal: string|undefined, repetend: string|undefined}}
     */
    #value = {
        integer: 0n,
        decimal: '1',
        repetend: undefined
    }
    /**
     * 
     */

    /**
     * @type {Rational|undefined}
     */
    #reciprocal = undefined

    /**
     * @type {Fraction|undefined}
     */
    #fraction = undefined

    /**
     * 
     * @param {BigInt} integer 
     * @param {string|undefined} decimal 
     * @param {string|undefined} repetend 
     */
    constructor(integer, decimal, repetend) {
        super("decimal")
        if (!integer instanceof BigInt)
            throw new Error('integer must be BigInt')
        if (!(decimal | repetend))
            throw new Error('decimal and repetend must have one')
        if (decimal && typeof decimal !== 'string')
            throw new Error('decimal must be a string')
        if (repetend && typeof repetend !== 'string')
            throw new Error('repetend must be a string')
        this.#value = { integer, decimal, repetend }
    }

    toString() {
        return this.#value.integer.toString() + '.' + this.#value.decimal + (this.#value.repetend ? '[' + this.#value.repetend + ']' : '')
    }

    toFraction() {
        if (!this.#fraction) {
            if (!this.#value.repetend) {
                this.#fraction = new Fraction(new Int(BigInt(this.#value.integer + this.#value.decimal)),
                new Int(BigInt('1'+'0'.repeat(this.#value.decimal.length)))).simplify()
            } else {
                if (!this.#value.decimal)
                    this.#fraction = new Fraction(new Int(BigInt(this.#value.repetend) + BigInt(this.#value.integer) * 
                        BigInt('9'.repeat(this.#value.repetend.length))),
                        new Int(BigInt('9'.repeat(this.#value.repetend.length)))).simplify().toFraction()
                else
                    this.#fraction = new Fraction(new Int(BigInt(this.#value.integer.toString() + this.#value.decimal + this.#value.repetend)
                        -BigInt(this.#value.integer.toString()+this.#value.decimal)),
                        new Int(BigInt('9'.repeat(this.#value.repetend.length)) * 10n ** BigInt(this.#value.decimal.length)))
                        .simplify().toFraction();
            }
        }
        return this.#fraction
    }

    /**
     * 
     * @param {Rational} value
     */
    add(value) {
        if(!value instanceof Rational)
            throw new Error('value must be Rational')
        if (value instanceof Decimal)
            return this.toFraction().add(value.toFraction())
        if (value instanceof Fraction)
            return this.toFraction().add(value)
    }

    /**
     * 
     * @param {Rational} value 
     */
    sub(value) {
        if (!value instanceof Rational)
            throw new Error('value must be Rational')
        if (value instanceof Decimal)
            return this.toFraction().sub(value.toFraction())
        if (value instanceof Fraction)
            return this.toFraction().sub(value)
    }

    /**
     * 
     * @param {Rational} value 
     * @returns 
     */
    mul(value) {
        if (!value instanceof Rational)
            throw new Error('value must be Rational')
        if (value instanceof Decimal) 
            return this.toFraction().mul(value.toFraction())
        if (value instanceof Fraction)
            return this.toFraction().mul(value)
    }

    /**
     * 
     * @param {Rational} value 
     * @returns 
     */
    div(value) {
        if (!value instanceof Rational)
            throw new Error('value must be Rational')
        if (value instanceof Decimal)
            return this.toFraction().div(value.toFraction())
        if (value instanceof Fraction)
            return this.toFraction().div(value)
    }

    /**
     * @returns {Int|Fraction|null}
     */
    get reciprocal() {
        if (this.#reciprocal)
            return this.#reciprocal
        this.#reciprocal = this.toFraction().reciprocal
        return this.#reciprocal
    }

    /**
     * @returns {{integer: BigInt, decimal: string|undefined, repetend: string|undefined}}
     */
    get value() {
        return this.#value
    }
}

export default Decimal